package com.benchmark.springboot.controller;


import com.benchmark.springboot.model.FileInfoVo;
import com.benchmark.springboot.utils.BeetlTemplateUtil;
import com.power.common.model.FileInfo;
import com.power.common.util.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.beetl.core.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * arthas火焰图浏览
 *
 * @author yu 2020/11/11
 */
@Slf4j
@RestController
public class ArthasOutPutController {

    private static final Logger LOGGER = LoggerFactory.getLogger(ArthasOutPutController.class);

    private static final String OUTPUT_PATH = "/arthas-output";

    @javax.annotation.Resource
    private Environment environment;

    /**
     * arthas火焰图列表
     *
     * @return
     * @page /arthas-output.html
     * @apiNote 返回一个展示火焰图文件的arthas-output.html
     */
    @GetMapping("arthas-output.html")
    public String render() {
        Template template = BeetlTemplateUtil.getByName("arthas-output.html");
        List<FileInfo> files = FileUtil.getFilesFromFolder(environment.getProperty("arthas.output.path", OUTPUT_PATH));
        List<FileInfoVo> fileInfoVos = new ArrayList<>(files.size());
        for (FileInfo fileInfo : files) {
            FileInfoVo fileInfoVo = new FileInfoVo();
            String fileName = fileInfo.getFileName();
            fileInfoVo.setFileName(fileName);
            if (fileName.length() > 45) {
                fileName = fileName.substring(0, 45) + "...";
            }
            fileInfoVo.setLink(fileName);
            fileInfoVo.setCreateTime(appendEmptyAtPre(fileInfo.getCreateTime(), 50 - fileName.length()));
            fileInfoVo.setStrSize(completionStart(String.valueOf(fileInfo.getFileSize()), 10));
            fileInfoVos.add(fileInfoVo);
        }
        template.binding("path", "arthas-output");
        template.binding("fileInfoList", fileInfoVos);
        return template.render();
    }

    /**
     * 预览火焰图
     *
     * @param fileName 文件名
     * @param request
     * @return
     * @throws IOException
     * @apiNote 点击火焰图列表即可显示，无需手动调用该接口。
     * 浏览器不可预览的文件则自动下载，可预览的文件直接实现阅览
     */
    @GetMapping("arthas-output/{fileName}")
    public ResponseEntity<Object> download(@PathVariable String fileName, HttpServletRequest request) throws IOException {
        Resource resource = loadFileAsResource(fileName);
        byte[] bytes = FileUtil.copyToByteArray(resource.getInputStream());
        // Try to determine file's content type
        String contentType = null;
        try {
            contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
        } catch (IOException ex) {
            LOGGER.info("Could not determine file type.");
        }
        LOGGER.info("contentType:{}", contentType);
        if (Objects.nonNull(contentType)) {
            // display
            if (contentType.contains("image") || contentType.contains("html") || contentType.contains("pdf")) {
                return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType))
                        .body(bytes);
            }
        }
        // Fallback to the default content type if type could not be determined
        if (contentType == null) {
            contentType = "application/octet-stream";
        }
        // download
        return ResponseEntity.ok()
                .contentType(MediaType.parseMediaType(contentType))
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + resource.getFilename() + "\"")
                .body(bytes);
    }

    private Resource loadFileAsResource(String fileName) {
        try {
            String path = environment.getProperty("arthas.output.path", OUTPUT_PATH);
            Path filePath = Paths.get(path)
                    .toAbsolutePath().normalize().resolve(fileName).normalize();
            Resource resource = new UrlResource(filePath.toUri());
            if (resource.exists()) {
                return resource;
            } else {
                throw new RuntimeException("File not found " + fileName);
            }
        } catch (MalformedURLException ex) {
            throw new RuntimeException("File not found " + fileName, ex);
        }
    }

    private String appendEmptyAtPre(String content, int size) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < size; i++) {
            sb.append(" ");
        }
        sb.append(content);
        return sb.toString();
    }

    private String completionStart(String content, int size) {
        int strLen = content.length();
        StringBuilder sb = new StringBuilder();
        if (strLen < size) {
            for (int i = 0; i < size - strLen; i++) {
                sb.append(" ");
            }
        }
        sb.append(content);
        return sb.toString();
    }
}
